﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using tinfosvr.Models;

namespace tinfosvr
{
    class TrainTimesUpdater
    {
        private List<train_schedule_item> mTrainSchedules = new List<train_schedule_item>();
        private List<qy_stationinfo> mPlatforms = new List<qy_stationinfo>();
        private Timer mTmrCheck = new Timer();
        private readonly int mTrainArriveDiff = -15;
        private static TrainTimesUpdater instance = new TrainTimesUpdater();

        private TrainTimesUpdater()
        {
            mTmrCheck.Interval = 1000.0;
            mTmrCheck.Elapsed += new ElapsedEventHandler(MTmrCheck_Elapsed);
        }

        private void DoUpdateTrainTimes()
        {
            try
            {
                using (mdfqyContext context = new mdfqyContext())
                {
                    List<qy_stationinfo> list = new List<qy_stationinfo>();
                    foreach (qy_stationinfo _stationinfo in mPlatforms)
                    {
                        if (UpdateTrainTimesInfo(_stationinfo))
                        {
                            list.Add(_stationinfo);
                        }
                    }
                    if (list.Count > 0)
                    {
                        context.qy_stationinfo.UpdateRange((IEnumerable<qy_stationinfo>)list);
                        context.SaveChanges();
                    }
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine("DoUpdateTrainTimes except:" + exception.Message);
            }
        }

        private int GetClosestIndexByLess(List<train_schedule_item> trains, int curSecondsInDay)
        {
            List<train_schedule_item> schedules = trains.OrderByDescending(x=>x.arriveTimeSecondsInDay).Where(x => x.arriveTimeSecondsInDay <= curSecondsInDay).ToList();
            if (!schedules.Any())
            {
                return -1;
            }
            return trains.IndexOf(schedules.First());
        }

        public static TrainTimesUpdater GetInstance() =>
            instance;

        public List<qy_stationinfo> GetPlatform(int citycode, string stationname) =>
            mPlatforms.Where(x=>x.citycode == citycode 
            && x.station_name.Equals(stationname)).ToList();

        private int GetSecondsInDay(string hhmm, int addDay)
        {
            hhmm = hhmm.Replace("：", ":");
            string[] strArray = hhmm.Split(':');
            return (((int.Parse(strArray[0]) * 0xe10) + (int.Parse(strArray[1]) * 60)) + (addDay * 0x15180));
        }

        public bool LoadData()
        {
            try
            {
                LoadPlatform();
                Console.WriteLine("========begin load train schedule=======");
                LoadTrainSchedule();
                Console.WriteLine("========end load train schedule=======");
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private void LoadPlatform()
        {
            using (mdfqyContext context = new mdfqyContext())
            {
                mPlatforms = context.qy_stationinfo.ToList();
            }
        }

        private void LoadTrainSchedule()
        {
            using (mdfqyContext context = new mdfqyContext())
            {
                foreach (qy_train_schedule _schedule in context.qy_train_schedule.ToList())
                {
                    train_schedule_item item = new train_schedule_item {
                        trainSchedule = _schedule,
                        arriveTimeSecondsInDay = GetSecondsInDay(_schedule.arrive_time, _schedule.arrive_day),
                        departTimeSecondsInDay = GetSecondsInDay(_schedule.departure_time, _schedule.arrive_day)
                    };
                    mTrainSchedules.Add(item);
                }
            }
        }

        public void MaintainTrainTimes(string path, int citycode)
        {
            StreamWriter writer = new StreamWriter("maintain" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".log");
            mdfqyContext context = new mdfqyContext();
            List<qy_city> cities = context.qy_city.Where(x =>
            x.citycode == citycode).ToList();
            if (cities.Count == 0)
            {
                Console.WriteLine("no city code:" + citycode);
                return;
            }
            string cityName = cities[0].name.Trim().Replace("市", "").ToLower();

            foreach (DirectoryInfo lineDir in new DirectoryInfo(path).GetDirectories())
            {
                string lineName = lineDir.Name.ToLower();
                string str = "====begin " + lineName;
                Console.WriteLine(str);
                List<qy_line> lineinfo = context.qy_line.Where(x =>
                x.cityCode == citycode
                && x.lid.ToLower().Contains(lineName)).ToList();

                if (lineinfo.Count == 0)
                {
                    str = "no line info:" + lineName;
                    Console.WriteLine(str);
                    writer.WriteLine(str);
                    continue;
                }
                writer.WriteLine(str);

                foreach (FileInfo stationFile in lineDir.GetFiles())
                {
                    str = "\t****begin " + stationFile.Name;
                    Console.WriteLine(str);
                    string[] nameParts = null;
                    if (stationFile.Name.Contains('-'))
                    {
                        nameParts = stationFile.Name.Split('-');
                        writer.WriteLine(str);
                        List<qy_stationline> stationInfo = context.qy_stationline.Where(x =>
                            x.ln.Contains(cityName)
                            && x.lid.ToLower().Contains(lineName)
                            && x.sid.Equals(nameParts[0])).ToList();

                        if (stationInfo.Count == 0)
                        {
                            str = "no this station info:" + nameParts[0];
                            Console.WriteLine(str);
                            writer.WriteLine(str);
                            continue;
                        }

                        StreamReader reader = new StreamReader(stationFile.FullName, Encoding.UTF8);
                        string strLine = reader.ReadLine();
                        int platformId = 0;
                        string termStaName = "";
                        string staName = nameParts[0];
                        string week = "0000000";
                        if (nameParts[1].Contains("工作日"))
                        {
                            week = "1111100";
                        }
                        else if (nameParts[1].Contains("双休日"))
                        {
                            week = "0000011";
                        }
                        while (strLine != null)
                        {
                            if (strLine.Trim().Length < 3)
                            {
                                strLine = reader.ReadLine();
                                continue;
                            }
                            if (new Regex(@"^\d+$").IsMatch(strLine.Trim()))
                            {
                                int num4 = 0;
                                string strCleanLine = strLine.Trim();
                                int startIndex = 0;
                                string strHour = strCleanLine.Substring(0, 1);
                                startIndex = 1;
                                if ((int.Parse(strHour) >= 0) && (int.Parse(strHour) <= 2))
                                {
                                    strHour = strCleanLine.Substring(0, 2);
                                    startIndex = 2;
                                    if ((int.Parse(strHour) == 0) || (int.Parse(strHour) == 0x18))
                                    {
                                        num4 = 1;
                                    }
                                }
                                string strMinute = "";
                                while (startIndex < strCleanLine.Length)
                                {
                                    strMinute = strCleanLine.Substring(startIndex, 2);
                                    qy_train_schedule entity = new qy_train_schedule
                                    {
                                        city_code = citycode,
                                        platform_id = platformId,
                                        origstation = staName,
                                        terminus = termStaName,
                                        arrive_time = strHour + ":" + strMinute,
                                        departure_time = "00:00",
                                        week = week,
                                        arrive_day = num4
                                    };
                                    context.qy_train_schedule.Add(entity);
                                    startIndex += 2;
                                }
                            }
                            else if (strLine.Contains("开往") && strLine.Contains("站"))
                            {
                                int index = strLine.IndexOf("开往");
                                int num7 = strLine.IndexOf("站");
                                string termName = strLine.Substring(index + 2, (num7 - index) - 2);
                                List<qy_stationline> lstTermStations = context.qy_stationline.Where(x =>
                                x.ln.Contains(cityName)
                                && x.lid.ToLower().Contains(lineName)
                                && x.sid.Equals(termName)).ToList();
                                if (lstTermStations.Count > 0)
                                {
                                    termStaName = lstTermStations[0].sid;
                                    int termStaid = lstTermStations[0].id;
                                    List<qy_stationinfo> platforms = context.qy_stationinfo.Where(x =>
                                        x.citycode == citycode
                                        && x.abb.ToLower().Equals(lineName)
                                        && x.station_name.Equals(nameParts[0])).ToList();
                                        
                                    if (platforms.Count == 0)
                                    {
                                        str = "no platform info:" + nameParts[0];
                                        Console.WriteLine(str);
                                        writer.WriteLine(str);
                                        continue;
                                    }

                                    foreach (qy_stationinfo si in platforms)
                                    {
                                        List<qy_stationline> directionStation = context.qy_stationline.Where(x =>
                                        x.ln.Contains(cityName)
                                        && x.lid.ToLower().Contains(lineName)
                                        && x.sid.Equals(si.direction)).ToList();

                                        if ((directionStation.Count > 0) && (((termStaid - stationInfo[0].id) ^ (directionStation[0].id - stationInfo[0].id)) >= 0))
                                        {
                                            platformId = si.id;
                                            break;
                                        }
                                    }
                                }
                            }
                            strLine = reader.ReadLine();
                        }
                        reader.Close();
                        context.SaveChanges();
                        str = "\t****end " + stationFile.Name;
                        Console.WriteLine(str);
                        writer.WriteLine(str);
                    }
                    else
                    {
                        str = "\terr: invalid file name" + stationFile.Name;
                        Console.WriteLine(str);
                        writer.WriteLine(str);
                    }
                }
                str = "====end " + lineName;
                Console.WriteLine(str);
                writer.WriteLine(str);
            }
  
            writer.Close();
        }

        private void MTmrCheck_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                mTmrCheck.Stop();
                DoUpdateTrainTimes();
            }
            catch (Exception exception)
            {
                Console.WriteLine("MTmrCheck_Elapsed expt:" + exception.Message);
            }
            finally
            {
                mTmrCheck.Start();
            }
        }

        public void StartUpdate()
        {
            mTmrCheck.Start();
        }

        public void StopUpdate()
        {
            mTmrCheck.Stop();
        }

        public bool UpdateTrainTimesInfo(qy_stationinfo info)
        {
            try
            {
                int weekDay = (int) DateTime.Now.DayOfWeek;
                if (weekDay == 0)
                {
                    weekDay = 7;
                }
                List<train_schedule_item> schedules = mTrainSchedules.Where(x =>
                x.trainSchedule.city_code == info.citycode
                && x.trainSchedule.platform_id == info.id
                && x.trainSchedule.week[weekDay - 1].Equals('1')).ToList().OrderBy(x=>x.arriveTimeSecondsInDay).ToList();
               
                if ((schedules == null) || (schedules.Count == 0))
                {
                    return false;
                }
                string str = Enumerable.First<train_schedule_item>((IEnumerable<train_schedule_item>) schedules).trainSchedule.arrive_time;
                string str2 = Enumerable.Last<train_schedule_item>((IEnumerable<train_schedule_item>) schedules).trainSchedule.arrive_time;
                string origstation = "";
                string terminus = "";
                int cur = -2;
                int nextTrain = -2;
                int closestIndexByLess = GetClosestIndexByLess(schedules, (int) DateTime.Now.TimeOfDay.TotalSeconds);
                if (closestIndexByLess < 0)
                {
                    cur = schedules[0].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds);
                    origstation = schedules[0].trainSchedule.origstation;
                    terminus = schedules[0].trainSchedule.terminus;
                    if (schedules.Count > 1)
                    {
                        nextTrain = schedules[1].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds);
                    }
                }
                else if (closestIndexByLess == (schedules.Count - 1))
                {
                    if ((schedules[closestIndexByLess].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds)) >= mTrainArriveDiff)
                    {
                        cur = 0;
                    }
                    origstation = schedules[closestIndexByLess].trainSchedule.origstation;
                    terminus = schedules[closestIndexByLess].trainSchedule.terminus;
                }
                else
                {
                    if ((schedules[closestIndexByLess].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds)) >= mTrainArriveDiff)
                    {
                        cur = 0;
                        if ((closestIndexByLess + 1) <= (schedules.Count - 1))
                        {
                            nextTrain = schedules[closestIndexByLess + 1].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds);
                        }
                    }
                    else
                    {
                        cur = schedules[closestIndexByLess + 1].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds);
                        if ((closestIndexByLess + 2) <= (schedules.Count - 1))
                        {
                            nextTrain = schedules[closestIndexByLess + 2].arriveTimeSecondsInDay - ((int) DateTime.Now.TimeOfDay.TotalSeconds);
                        }
                    }
                    origstation = schedules[closestIndexByLess].trainSchedule.origstation;
                    terminus = schedules[closestIndexByLess].trainSchedule.terminus;
                }
                info.direction = terminus;
                info.first_time = str;
                info.last_time = str2;
                info.points = origstation + "-" + terminus;
                info.current_train = cur;
                info.next_train = nextTrain;
                return true;
            }
            catch (Exception exception)
            {
                object[] objArray1 = new object[] { "UpdateTrainTimesInfo except:", info.station_name, ",", (int) info.id, ",", exception.Message };
                Console.WriteLine(string.Concat((object[]) objArray1));
            }
            return false;
        }
    }

    class train_schedule_item
    {
        public qy_train_schedule trainSchedule;
        public int arriveTimeSecondsInDay;
        public int departTimeSecondsInDay;
    }
}

